/*
Copyright (C) 2009-2010 Xanadu Development Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2
of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "SkillLevelData.h"
#include "PlayerInventory.h"
#include "DataProvider.h"
#include "PartyMembers.h"
#include "PlayerHandler.h"
#include "PacketReader.h"
#include "PacketCreator.h"
#include "PlayerSkills.h"
#include "PlayerBuffs.h"
#include "Player.h"
#include "Item.h"
#include "Party.h"
#include "Skill.h"
#include "Effect.h"
#include "SkillData.h"
#include "Summons.h"
#include "Map.h"
#include "World.h"

void PlayerHandler::addSkillHandle(PacketReader& packet) {
	packet.read(4);
	int skillid = packet.readInt();
	if(!Beginner_skill(skillid)) {
		if(player->getSp() == 0) {
			return;
		}
		player->setSp(player->getSp() - 1);
	}
	Skill* skill = player->getSkill(skillid);
	if (skill->getLevel() == skill->getMasterLevel()) {
		return;
	}
	skill->setLevel(skill->getLevel() + 1);
	player->send(PacketCreator().updateSkill(skill));
}

void PlayerHandler::stopSkillHandle(PacketReader& packet) {
	int skillid = packet.readInt();
	switch (skillid) {
		case 3121004:
		case 13111002:
        case 3221001:
		case 2121001:
		case 2221001:
		case 2321001:
		case 5221004: // Special Skills
			player->getMap()->send(PacketCreator().endSkillEffect(player, skillid));
			break;
		case 9101004: // GM Hide
			player->getMap()->send(PacketCreator().showPlayer(player));
			player->send(PacketCreator().endHide());
			break;
		case 1301007: // Hyperbody
		case 9101008:
			player->setMaxHp(player->getBaseMaxHp());
			player->setMaxMp(player->getBaseMaxMp());
			if (player->getHp() > player->getMaxHp()) {
				player->setHp(player->getMaxHp());
			}
			if (player->getMp() > player->getMaxMp()) {
				player->setMp(player->getMaxMp());
			}
			break;
	}
	if (Summons::isSummon(skillid)) {
		player->getSummons()->removeSummon(skillid);
	}
	player->getBuffs()->cancelBuff(skillid);
}

void PlayerHandler::useSkillHandle(PacketReader &packet) {
	packet.read(4);
	int skillid = packet.readInt();
	char level = packet.read();
	SkillLevelData* skill = DataProvider::getInstance()->getSkillData(skillid)->getDataByID(level);
	if (player->getSummons()->isSummon(skillid)) {
		player->getSummons()->addSummon(skillid, level, skill->getX());
	}
	Effect *effect = Effect::getEffect(skillid, level);
	if(effect != 0) {
		effect->useSkill(player);
	}
	switch(skillid) {
		case 1004: // Monster Rider
		case 10001004:
		case 20001004: {
			Item *mount = player->getInventories()->getItemBySlot(EQUIPPED, -18);
			if (mount != 0) {
				player->getMap()->send(PacketCreator().useMount(player->getID(), skillid, mount->getID()));
			}
			break;
		}
		case 5101005: { // MpRecovery
			short modhp = player->getMaxHp() * skill->getX() / 100;
			short healmp = modhp * skill->getY() / 100;
			player->setHp(player->getHp() - modhp);
			player->setMp(player->getMp() + healmp);
			break;
		}
		case 1121001: // Monster Magnet
		case 1221001:
		case 1321001: {
			int mobs = packet.readInt();
			for (int k = 0; k < mobs; k++) {
				int mapmobid = packet.readInt();
				char success = packet.read();
				player->getMap()->send(PacketCreator().showMagnetSuccess(mapmobid, success));
			}
			packet.read();
			break;
		}
		case 2301002: { // Heal
			unsigned short healrate = skill->getHpPer();
			if (healrate > 100)
				healrate = 100;

			Party *party = player->getParty();
			char partyPlayers = (party != 0 ? (char)player->getParty()->getMembers()->getPlayers()->size() : 1);
			int expIncreasement = 0;

			short heal = (healrate * player->getMaxHp() / 100) / partyPlayers;

			if (party != 0) {
				hash_map <int, Player*>* ps = player->getParty()->getMembers()->getPlayers();
				for (hash_map<int,Player*>::iterator iter = ps->begin(); iter != ps->end(); iter++) {
					if (iter->second->getMap() == player->getMap()) {
						Position pos = player->getPosition();
						Position fpos = iter->second->getPosition();
						if (fpos.x > pos.x + skill->getLT().x && fpos.y > pos.y + skill->getLT().y && fpos.x < pos.x + skill->getRB().x && fpos.y < pos.y + skill->getRB().y) {
							short chp = iter->second->getHp();
							if (chp != 0 && chp < iter->second->getMaxHp()) {
								iter->second->addHp(heal);
								if (player != iter->second) {
									expIncreasement += 20 * (iter->second->getHp() - chp) / (8 * iter->second->getLevel() + 190);
								}
							}
						}
					}
				}
				if (expIncreasement > 0) {
					player->giveExp(expIncreasement);
				}
			}
			else {
				if (player->getHp() < player->getMaxHp())
					player->addHp(heal);
			}
			break;
		}
		case 2321006: { // resurrection
			if (player->getParty() != 0) {
				char affected = packet.read();
				hash_map <int, Player*>* ps = player->getParty()->getMembers()->getPlayers();
				for(hash_map<int,Player*>::iterator iter = ps->begin(); iter != ps->end(); iter++) {
					if(affected && iter->second != player && iter->second->getMap() == player->getMap() && iter->second->getHp() == 0) {
						iter->second->send(PacketCreator().showPlayerEffect(2, skillid));
						iter->second->setHp(iter->second->getMaxHp());
					}
				}
			}
			break;
		}
		case 9101000: { // gm heal & dispel
			char players = packet.read();
			for (int i = 0; i < players; i++) {
				int playerid = packet.readInt();
				Player *target = player->getWorld()->getPlayerByID(playerid);
				if (target != 0 && target != player && target->getHp() > 0) {
					target->setHp(target->getMaxHp());
					target->setMp(target->getMaxMp());
					// todo dispel
				}
			}
			break;
		}
		case 9101005: { // resurrection
			char players = packet.read();
			for (int i = 0; i < players; i++) {
				int playerid = packet.readInt();
				Player *target = player->getWorld()->getPlayerByID(playerid);
				if (target != 0 && target != player && target->getHp() == 0) {
					target->setHp(target->getMaxHp());
				}
			}
			break;
		}
		case 9101004: // GM Hide
			player->getMap()->send(PacketCreator().removePlayer(player));
			player->send(PacketCreator().beginHide());
			break;
		default:
			break;
	}
}

void PlayerHandler::handleSpecialSkills(PacketReader &packet) {
	int skillid = packet.readInt();
	switch (skillid) {
		case 1121001: // Monster Magnet x3
		case 1221001:
		case 1321001:
		case 3221001: // Pierce
		case 2121001: // Big Bang x3
		case 2221001:
		case 2321001: {
			char level = packet.read();
			char speed = packet.read();
			char flags = packet.read();
			player->getMap()->send(PacketCreator().skillEffect(player, skillid, level, flags, speed));
			break;
		}
		case 4211001: { // Chakra
			SkillLevelData* skill = DataProvider::getInstance()->getSkillData(skillid)->getDataByID(player->getSkill(skillid)->getLevel());
			short dex = player->getDex();
			short luk = player->getLuk();
			short recovery = skill->getY();
			short maximum = (luk * 66 / 10 + dex) * 2 / 10 * (recovery / 100 + 1);
			short minimum = (luk * 33 / 10 + dex) * 2 / 10 * (recovery / 100 + 1);
			short range = maximum - minimum;
			player->setHp(player->getHp() + (Tools::random(range) + minimum));
			break;
		}
	}
}